package com.xstudio.pvzreborn.data.custom;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.xstudio.pvzreborn.PVZReborn;
import com.xstudio.pvzreborn.recipe.machine.polarcraftingtable.PCTRecipeBase;
import com.xstudio.pvzreborn.recipe.machine.polarcraftingtable.PCTShapedRecipe;
import com.xstudio.pvzreborn.recipe.machine.polarcraftingtable.PCTShapelessRecipe;
import com.xstudio.pvzreborn.utils.Utils;
import net.minecraft.data.recipes.FinishedRecipe;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.level.ItemLike;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.Map.Entry;
import java.util.function.Consumer;

/**
 * @author Bread_NiceCat
 */
public class PCTFinishedRecipe extends BaseFinishedRecipe<PCTRecipeBase> {

	private final List<String> pattern;
	private final Map<Character, Ingredient> defines;

	public PCTFinishedRecipe(PCTRecipeBase recipe, Map<Character, Ingredient> defines, List<String> pattern) {
		super(recipe);
		this.defines = defines;
		this.pattern = pattern;
	}

	public static Builder builder() {
		return new Builder();
	}

	@Override
	public String getTypeId() {
		return PCTRecipeBase.TYPE_ID;
	}

	@Override
	public void serializeRecipeData(@NotNull JsonObject pJson) {
		if (!recipe.isShapeless(recipe)) {
			JsonArray patternJson = new JsonArray();
			JsonObject defineJson = new JsonObject();

			for (String s : this.pattern) {
				patternJson.add(s);
			}

			pJson.add("pattern", patternJson);

			for (Entry<Character, Ingredient> entry : this.defines.entrySet()) {
				defineJson.add(String.valueOf(entry.getKey()), entry.getValue().toJson());
			}
			pJson.add("defines", defineJson);
		} else {
			JsonArray inputJson = new JsonArray();
			for (Ingredient input : recipe.getInputs()) {
				inputJson.add(input.toJson());
			}
			pJson.add("inputs", inputJson);
		}
		pJson.add("output", Utils.serializeStack(recipe.getResultItem()));
		pJson.addProperty("requireEnergy", recipe.getRequireEnergy());
		pJson.addProperty("tick", recipe.getTick());
		pJson.addProperty("isShaped", !recipe.isShapeless(recipe));
	}

	public static class Builder {

		private final Map<Character, Ingredient> defines = new HashMap<>(4);
		private final List<String> pattern = new ArrayList<>(3);
		private final List<Ingredient> inputs;
		private ResourceLocation id;
		private ItemStack output;
		private boolean isShapeless;
		private int requireEnergy;
		private int tick;

		private Builder() {
			inputs = new ArrayList<>(9);
		}

		public Builder addInput(Ingredient input) {
			if (!isShapeless) {
				throw new IllegalStateException("I AM NOT A SHAPELESS RECIPE :(");
			}
			inputs.add(input);
			return this;
		}

		public Builder addInput(ItemStack input) {
			return addInput(Ingredient.of(input));
		}

		public Builder addInput(ItemLike input) {
			return addInput(Ingredient.of(input));
		}

		public Builder shapeless() {
			isShapeless = true;
			return this;
		}

		public Builder output(Item output) {
			return output(output.getDefaultInstance());
		}

		public Builder output(Item output, int count) {
			return output(new ItemStack(output, count));
		}

		public Builder output(ItemStack output) {
			this.output = output;
			this.id = PVZReborn.prefix(output.getItem().getRegistryName().getPath());
			return this;
		}

		public Builder pattern(String pat) {
			if (isShapeless) {
				throw new IllegalStateException("I AM NOT A SHAPED RECIPE :(");
			}
			if (!pattern.isEmpty() && pat.length() != pattern.get(0).length()) {
				throw new IllegalStateException("Pattern length is different :(");
			}
			pattern.add(pat);
			return this;
		}

		public Builder define(char c, ItemLike itemLike) {
			return define(c, Ingredient.of(itemLike));
		}

		public Builder define(char c, Ingredient ingredient) {
			if (isShapeless) {
				throw new IllegalStateException("I AM NOT A SHAPED RECIPE :(");
			}
			defines.put(c, ingredient);
			return this;
		}

		public Builder requireEnergy(int requireEnergy) {
			this.requireEnergy = requireEnergy;
			return this;
		}

		public Builder tick(int tick) {
			this.tick = tick;
			return this;
		}

		public void build(Consumer<FinishedRecipe> consumer) {
			if (!isShapeless) {
				resolve();
				new PCTFinishedRecipe(new PCTShapedRecipe(id, output, inputs, requireEnergy, tick), defines, pattern).save(consumer);
			} else {
				new PCTFinishedRecipe(new PCTShapelessRecipe(id, output, inputs, requireEnergy, tick), defines, pattern).save(consumer);
			}
		}

		private void resolve() {
			for (String line : pattern) {
				char[] chars = line.toCharArray();
				for (char definedCh : chars) {
					if (definedCh == ' ') {
						inputs.add(Ingredient.EMPTY);
					} else {
						inputs.add(Objects.requireNonNull(defines.get(definedCh), "undefined char: " + definedCh));
					}
				}
			}
		}
	}

}
